require 'sessionabstract'

module ResultsetAutofilter

  # Constants for using in yours filters that you can easily modify

   ViewSexe = {
          'Masculin' => '1',
          'Féminin' => '2'
        }
   ViewBool = {
          'Oui' => '1',
          'Non' => '0'
        }
   ViewDateFilter = {
          'Inférieur à' => 'lower',
          'Supérieur à' => 'upper',
          'Egal à' => 'equal',
          'Compris entre' => 'between'
        }
   ViewMeasureFilter = {
          'Inférieur à' => 'lower',
          'Egal à' => 'equal',
          'Supérieur à' => 'upper'
        }
   ViewEmpty = {
          'Oui' => 'Y',
          'Non' => 'N'
        }
   ViewEmptyZero = {
          'Oui' => 'Y_0000000000',
          'Non' => 'N_0000000000'
        }
   ViewStringFilter = {
          'Commence par' => 'begin',
          'Contient' => 'contain',
          'Egal à' => 'equal',
          'Finit par' => 'finished'
        }

    Month = {
      1 => "janvier",
      2 => "F&#233;vrier",
      3 => "Mars",
      4 => "Avril",
      5 => "Mai",
      6 => "Juin",
      7 => "Juillet",
      8 => "Ao&#251;t",
      9 => "Septembre",
      10 =>"Octobre",
      11 => "Novembre",
      12 => "D&#233;cembre"
    }


  # Session stack
  class Filters_and_conditions
    @@session_object = nil

    def self.init(controller_object, index, tables_name, tags_dynamic_connection=nil)
        @@session_object = Sessionabstract.instance
        if !@@session_object.get_object(index)
           @@session_object.set_object(controller_object, index, Generate_condition.new(tables_name, tags_dynamic_connection))
        end
      #      controller_object.session[index] = Generate_condition.new(tables_name, tags_dynamic_connection)
    end

    # Delete the session of a page
    def self.delete(controller_object, index)
      #      controller_object.session[index] = nil
      @@session_object.delete(controller_object, index)
    end

    # Get the session of a page
    def self.get_object(index)
      #      controller_object.session[index]
      @@session_object.get_object(index)
    end


    # Get the value of an object
    def self.get_value(controller_object, index, val)
      # controller_object.session[index]
      @@session_object.get_value(controller_object, index, val)
    end


    # Set the hash value of an dynamic connection
    def self.set_connection(controller_object, index, h)
      #  controller_object.session[index]
      o = @@session_object.get_object(index)
      o.set_connection(h)
      @@session_object.commit_object(controller_object, index, o)
    end



    # Create the search condition
    def self.generate(controller_object, params, index)
      #      controller_object.session[index].create(params)
      o = @@session_object.get_object(index)
      o.create(params)
      @@session_object.commit_object(controller_object, index, o)
    end

    # Duck typing : calling method_missing
    def self.conditions(controller_object, index)
      # controller_object.session[index].conditions
      o = @@session_object.get_object(index)
      o.conditions
    end


    def self.check_field(controller_object, index, field)
      #  controller_object.session[index].state_filters[field]
      o = @@session_object.get_object(index)
      o.state_filters[field]
    end

    # Instance the fields stored in the session object
    def self.instance_fields(controller_object, index)
      o = @@session_object.get_object(index)
      fields = o.fields
      #   fields = controller_object.session[index].fields
      if fields.length != 0
        fields.each_key do |key|
          controller_object.instance_variable_set("@#{key}".intern, fields[key])
        end
      end
      fields.length
    end

    def self.get_field(controller_object, index, field)
      o = @@session_object.get_object(index)
      begin
        # controller_object.session[index].fields[field]
        o.fields[field]
      rescue
        nil
      end
    end


    # Store fields's values into the session object
    def self.set_fields(controller_object, index, fields)
      o = @@session_object.get_object(index)
      o.fields = fields
      @@session_object.commit_object(controller_object, index, o)
      #  controller_object.session[index].fields = fields
      fields.each do |key, value|
        controller_object.instance_variable_set("@#{key}".intern, value)
      end
    end


    # Store a field's value into the session object
    def self.add_or_modify_field(controller_object, index, field)
      #  controller_object.session[index].fields[field[0]] = field[1]
        o = @@session_object.get_object(index)
        o.fields[field[0]] = field[1]
        @@session_object.commit_object(controller_object, index, o)
    end


    # Delete a field's value
    def self.delete_field(controller_object, index, field)
      # controller_object.session[index].fields.delete(field) if controller_object.session[index].fields[field]
        o = @@session_object.get_object(index)
        o.fields.delete(field) if o.fields[field]
        @@session_object.commit_object(controller_object, index, o)
    end

    # Delete page's fields
    def self.reset_fields(controller_object, index)
      # controller_object.session[index].fields.clear
        o = @@session_object.get_object(index)
        o.fields.clear
        @@session_object.commit_object(controller_object, index, o)
    end




  end

  #######
  private
  #######


  class Generate_condition

      attr_accessor :fields
      attr_reader :state_filters
      attr_accessor :data

      # this is an hash that associate key words with an sql directive
      @@assocFilter = {
        :begin     => ["LIKE '","%'"],
        :contain   => ["LIKE '%","%'"],
        :equal     => ["='","'"],
        :not_equal => ["<> '","'"],
        :in        => ["IN ('", "')"],
        :lower     => ["< '","'"],
        :upper     => ["> '","'"],
        :finished  => ["LIKE '%","'"],
        :between   => [""]
      }



    def initialize(tables_name, tags_dynamic_connection)
      @conditions = ''
      @conditions_between = ''
      @select_and_input = Hash.new
      @select = Hash.new
      @input = Hash.new
      @select_with_bool = Hash.new
      @datebetween = Hash.new
      @stack_datebetween = Hash.new
      @filters = Hash.new
      @fields = Hash.new

      # store the state of a value
      @state_filters = Hash.new
      @tables_name =  tables_name
      @tags_dynamic_connection = tags_dynamic_connection

      # store some personal data
      @data = Hash.new
    end


   def get_connection
      #return an hash of the current object that permit a dynamic sgbd connection
     @tags_dynamic_connection
   end


   def set_connection(h)
    # set an hash in the current object  that permit a dynamic sgbd connection
    @tags_dynamic_connection = h
   end

     # This method stores if the value from params is different of the old value or not
     def state_filter(filter, value)
       if @filters[filter.to_sym] != value
        @state_filters[filter.to_sym] = false
       else
        @state_filters[filter.to_sym] = true
       end
     end



    def create(params)
      # construct Hash with params
      @conditions = ''
      @conditions_between = ''

      params.each do |key, value|
        field = key.split('_')
        case field[0]
        when 'selectandinput'
          @select_and_input[field[1]] = value
          value.each do |k, v|
            lk = key + '_' + k
            state_filter(lk, v)
          end
        when 'select'
          @select[field[1]] = value
          value.each do |k, v|
            lk = key + '_' + k
            state_filter(lk, v)
          end

        when 'input'
          @input[field[1]] = value
          value.each do |k, v|
            lk = key + '_' + k
            state_filter(lk, v)
          end
        when 'datebetween'
          @datebetween[field[1]] = value
          value.each do |k, v|
            lk = key + '_' + k
            state_filter(lk, v)
          end
        when 'selectwithbool'
          @select_with_bool[field[1]] = value
          value.each do |k, v|
            lk = key + '_' + k
            state_filter(lk, v)
          end
        end
      end

      # extract the input fields
      @input.each do |key, value|
        value.each do |field, data|
          index = 'input_' + key + '_' + field
          indexselect = 'selectandinput_' + key + '_' + field
          self.send index, data


          if data != ''
              filter = @select_and_input[key][field]
              self.send indexselect, filter
              filter_before = @@assocFilter[filter.to_sym][0]
              filter_after = @@assocFilter[filter.to_sym][1]
              @conditions << ' AND ' if @conditions.size > 0
              @conditions << @tables_name[key.to_sym] + "." + field + " #{filter_before}#{data}#{filter_after}"
          end
        end
      end



      # extract the select fields
      @select.each do |key, value|
        value.each do |field, data|
          indexselect = 'select_' + key + '_' + field
          self.send indexselect, data

          if data.length == 1
              @conditions << ' AND ' if @conditions.size > 0
              @conditions << @tables_name[key.to_sym] + "." + field + " = #{data}"
          elsif data != '-1'
              @conditions << ' AND ' if @conditions.size > 0
              @conditions << @tables_name[key.to_sym] + "." + field + " = '#{data}'"
          end
        end
      end






      # extract the select_bool fields
      @select_with_bool.each do |key, value|
        value.each do |field, data|
          indexbool = 'selectwithbool_' + key + '_' + field
          self.send indexbool, data

          if data.length == 1
              @conditions << ' AND ' if @conditions.size > 0
              @conditions << @tables_name[key.to_sym] + "." + field + " = #{data}"
          elsif data != '-1'
            @conditions << ' AND ' if @conditions.size > 0
            @conditions << @tables_name[key.to_sym] + "." + field + " #{data}"
          end
        end
      end





      # extract the date fields
      @datebetween.each do |key, value|
        value.each do |field, data|
          index = 'datebetween_' + key + '_' + field
          indexselect = 'select_' + key + '_' + field.gsub(/begin_/,'')
          self.send index, data

          filter = @select[key][field.gsub(/begin_/,'')]
          self.send indexselect, filter

          if data != '' &&  !@stack_datebetween[field]



           if field.match(/begin_/)
             begin_field = field.sub('begin_', '')
             filter = @select[key][begin_field]
           elsif field.match(/end_/)
             end_field = field.sub('end_', '')
             filter = @select[key][end_field]
           end


           if filter == 'between'
              if begin_field
                @conditions_between << ' AND ' if @conditions_between.size > 0 || @conditions.size > 0
              # une fois la date de début obtenu on va chercher la date de fin que l'on ajoute dans la stack
              # de manière à l'éviter à la prochaine itération
                @conditions_between << @tables_name[key.to_sym] + "." + begin_field + " BETWEEN '#{data}' AND '" +
                                        @datebetween[key]['end_' + begin_field] + "'"
                @stack_datebetween['end_' + begin_field] = true
              elsif end_field
                @conditions_between << ' AND ' if @conditions_between.size > 0 || @conditions.size > 0
              # Si la date de fin vient en premier on va chercher la date de début que l'on ajoute dans la stack
              # de manière à l'éviter à la prochaine itération
                @conditions_between << @tables_name[key.to_sym] + "." + end_field + " BETWEEN '" +
                                       @datebetween[key]['begin_' + end_field] + "' AND '#{data}'"
                 @stack_datebetween['begin_' + end_field] = true
              end
           elsif begin_field
              @conditions_between << ' AND ' if @conditions_between.size > 0 || @conditions.size > 0
              filter_before = @@assocFilter[filter.to_sym][0]
              filter_after = @@assocFilter[filter.to_sym][1]
              @conditions_between << @tables_name[key.to_sym] + "." + begin_field + " #{filter_before}#{data}#{filter_after}"
           end


          end
        end
      end


      @conditions <<   @conditions_between if @conditions_between != ''


      if @conditions == ''
        @filters[:conditions] = nil
      else
        @filters[:conditions] = @conditions
      end

     end



    def method_missing(id, *args)
      method = id.id2name

      if args.first
        @filters[method.to_sym] = args.first
      else
        @filters[method.to_sym]
      end
    end


  end
end
